@file:OptIn(ExperimentalUuidApi::class)

package com.harth.blemultiplatform.ble

import com.harth.blemultiplatform.ble.core.BondState
import com.harth.blemultiplatform.ble.core.ConnectionParameters
import com.harth.blemultiplatform.ble.core.ConnectionPriority
import com.harth.blemultiplatform.ble.core.PeripheralType
import com.harth.blemultiplatform.ble.core.Phy
import com.harth.blemultiplatform.ble.core.PhyInUse
import com.harth.blemultiplatform.ble.core.PhyOption
import com.harth.blemultiplatform.ble.core.WriteType
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import no.nordicsemi.kotlin.ble.client.android.Peripheral
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid


class NativePeripheral(val peripheral: Peripheral, val scope: CoroutineScope) : IPeripheral {
    override val name: String?
        get() = peripheral.name

    override val address: String
        get() = peripheral.address

    override val bondState: StateFlow<BondState> = peripheral.bondState
        .map { bondState ->
            when (bondState) {
                no.nordicsemi.kotlin.ble.core.BondState.NONE -> BondState.NONE
                no.nordicsemi.kotlin.ble.core.BondState.BONDING -> BondState.BONDING
                no.nordicsemi.kotlin.ble.core.BondState.BONDED -> BondState.BONDED
            }
        }
        .stateIn(scope = scope, SharingStarted.Eagerly, BondState.NONE)

    override val connectionParameters: StateFlow<ConnectionParameters?> =
        peripheral.connectionParameters.map {
            when (it) {
                is no.nordicsemi.kotlin.ble.core.ConnectionParameters.Connected -> ConnectionParameters.Connected(
                    connectionInterval = it.connectionInterval,
                    slaveLatency = it.slaveLatency,
                    supervisionTimeout = it.supervisionTimeout
                )

                no.nordicsemi.kotlin.ble.core.ConnectionParameters.Unknown -> ConnectionParameters.Unknown
                null -> null
            }
        }.stateIn(scope, SharingStarted.Eagerly, null)

    override val phy: StateFlow<PhyInUse?> = peripheral.phy.map {
        it?.toPhyInUser()
    }.stateIn(scope, SharingStarted.Eagerly, null)

    override val type: PeripheralType
        get() = when (peripheral.type) {
            no.nordicsemi.kotlin.ble.core.PeripheralType.CLASSIC -> PeripheralType.CLASSIC
            no.nordicsemi.kotlin.ble.core.PeripheralType.LE -> PeripheralType.LE
            no.nordicsemi.kotlin.ble.core.PeripheralType.DUAL -> PeripheralType.DUAL
            no.nordicsemi.kotlin.ble.core.PeripheralType.UNKNOWN -> PeripheralType.UNKNOWN
        }

    override suspend fun readPhy(): PhyInUse {
        return peripheral.readPhy().toPhyInUser()
    }

    override suspend fun createBond() {
        peripheral.createBond()
    }

    override suspend fun removeBond() {
        peripheral.removeBond()
    }

    override suspend fun requestConnectionPriority(priority: ConnectionPriority): ConnectionParameters {
        return peripheral.requestConnectionPriority(
            when (priority) {
                ConnectionPriority.BALANCED -> no.nordicsemi.kotlin.ble.client.android.ConnectionPriority.BALANCED
                ConnectionPriority.HIGH -> no.nordicsemi.kotlin.ble.client.android.ConnectionPriority.HIGH
                ConnectionPriority.LOW_POWER -> no.nordicsemi.kotlin.ble.client.android.ConnectionPriority.LOW_POWER
                ConnectionPriority.DIGITAL_CAR_KEY -> no.nordicsemi.kotlin.ble.client.android.ConnectionPriority.DIGITAL_CAR_KEY
            }
        ).let {
            when (it) {
                is no.nordicsemi.kotlin.ble.core.ConnectionParameters.Connected -> ConnectionParameters.Connected(
                    connectionInterval = it.connectionInterval,
                    slaveLatency = it.slaveLatency,
                    supervisionTimeout = it.supervisionTimeout
                )

                no.nordicsemi.kotlin.ble.core.ConnectionParameters.Unknown -> ConnectionParameters.Unknown
            }
        }
    }

    override suspend fun setPreferredPhy(
        txPhy: Phy,
        rxPhy: Phy,
        phyOptions: PhyOption
    ): PhyInUse {
        return peripheral.setPreferredPhy(
            txPhy.toPhy(), rxPhy.toPhy(), when (phyOptions) {
                PhyOption.NO_PREFERRED -> no.nordicsemi.kotlin.ble.core.PhyOption.NO_PREFERRED
                PhyOption.S2 -> no.nordicsemi.kotlin.ble.core.PhyOption.S2
                PhyOption.S8 -> no.nordicsemi.kotlin.ble.core.PhyOption.S8
            }
        ).toPhyInUser()
    }

    override fun maximumWriteValueLength(type: WriteType): Int {
        return peripheral.maximumWriteValueLength(type.toWriteType())
    }

    override fun services(uuids: List<Uuid>): StateFlow<List<IService>?> {
        val flow = peripheral.services(uuids)
        val filteredServices =
            flow.value?.filter { service -> uuids.any { it == service.uuid } }?.map {
                NativeService(
                    service = it,
                    peripheral = this
                )
            }
        return peripheral.services(uuids).map {
            it?.map {
                NativeService(
                    service = it,
                    peripheral = this
                )
            }
        }.stateIn(scope, SharingStarted.Eagerly, filteredServices)
    }


    override suspend fun readRssi(): Int {
        return peripheral.readRssi()
    }

    override suspend fun disconnect(): Boolean {
        peripheral.disconnect()
        return true
    }

    override fun close() {

    }
}

fun no.nordicsemi.kotlin.ble.core.PhyInUse.toPhyInUser(): PhyInUse {
    return PhyInUse(
        txPhy = when (txPhy) {
            no.nordicsemi.kotlin.ble.core.Phy.PHY_LE_1M -> Phy.PHY_LE_1M
            no.nordicsemi.kotlin.ble.core.Phy.PHY_LE_2M -> Phy.PHY_LE_2M
            no.nordicsemi.kotlin.ble.core.Phy.PHY_LE_CODED -> Phy.PHY_LE_CODED
        },
        rxPhy = when (rxPhy) {
            no.nordicsemi.kotlin.ble.core.Phy.PHY_LE_1M -> Phy.PHY_LE_1M
            no.nordicsemi.kotlin.ble.core.Phy.PHY_LE_2M -> Phy.PHY_LE_2M
            no.nordicsemi.kotlin.ble.core.Phy.PHY_LE_CODED -> Phy.PHY_LE_CODED
        }
    )
}

fun Phy.toPhy(): no.nordicsemi.kotlin.ble.core.Phy {
    return when (this) {
        Phy.PHY_LE_1M -> no.nordicsemi.kotlin.ble.core.Phy.PHY_LE_1M
        Phy.PHY_LE_2M -> no.nordicsemi.kotlin.ble.core.Phy.PHY_LE_2M
        Phy.PHY_LE_CODED -> no.nordicsemi.kotlin.ble.core.Phy.PHY_LE_CODED
    }
}

fun no.nordicsemi.kotlin.ble.core.Phy.toPhy(): Phy {
    return when (this) {
        no.nordicsemi.kotlin.ble.core.Phy.PHY_LE_1M -> Phy.PHY_LE_1M
        no.nordicsemi.kotlin.ble.core.Phy.PHY_LE_2M -> Phy.PHY_LE_2M
        no.nordicsemi.kotlin.ble.core.Phy.PHY_LE_CODED -> Phy.PHY_LE_CODED
    }
}

fun WriteType.toWriteType(): no.nordicsemi.kotlin.ble.core.WriteType {
    return when (this) {
        WriteType.WITH_RESPONSE -> no.nordicsemi.kotlin.ble.core.WriteType.WITH_RESPONSE
        WriteType.WITHOUT_RESPONSE -> no.nordicsemi.kotlin.ble.core.WriteType.WITHOUT_RESPONSE
        WriteType.SIGNED -> no.nordicsemi.kotlin.ble.core.WriteType.SIGNED
    }
}

fun no.nordicsemi.kotlin.ble.core.WriteType.toWriteType(): WriteType {
    return when (this) {
        no.nordicsemi.kotlin.ble.core.WriteType.WITH_RESPONSE -> WriteType.WITH_RESPONSE
        no.nordicsemi.kotlin.ble.core.WriteType.WITHOUT_RESPONSE -> WriteType.WITHOUT_RESPONSE
        no.nordicsemi.kotlin.ble.core.WriteType.SIGNED -> WriteType.SIGNED
    }
}